/* main.c - the heart of the framework */
/*  handles all standard initialization */

/*  check common.h for compile switches */
#include "common.h"

/* Include all the sub-items here */
	// error handler
#include "error.h"
	// full-screen movie playback
#include "movie.h"
	// full-screen UI widget
#include "ui.h"
	// loading screen
#include "init_game.h"
	// game handler
//#include "game.h"
	// in-game hangar
//#include "hangar.h"

	// definitions for UI screens (main, careers, load, multiplayer, credits)
#include "ui_screen.h"

// This is some control flow and UI stuff
//  it's shared across all the subsystems

// Helper: computed Screen Aspect Ratio
GLfloat aspect;
// current mouse X / Y location
int mx, my;
// Holds a display list for blitting the mouse cursor
GLuint cursor_list;
// Font structures for writing to the screen, there are four fonts in game
//  (small, med, large, and money / gauge numbers)
struct font *font[4];

// Pointer to a cfg_struct structure
struct cfg_struct *cfg;

// Helper: shared "clunk" sound effect used in multiple places
Mix_Chunk *sfx_click = NULL;

// Flags pasesd to sdl_mixer and sdl_image
int mix_flags;
int image_flags;

// Other global data goes here.
//   These would be things like parameters to the state initializers, etc
char career_movie[256];
char error_message[256]; // A place for writing an error_message, before
//  kicking it to GAMESTATE_ERROR

/******************************************************************************/

// Returns a cfg structure with defaults set
struct cfg_struct *set_cfg_defaults()
{
    struct cfg_struct *temp = cfg_init();
    char temp_string[20] = "";

    // set defaults
    // GFX settings
    cfg_set(temp,"SCREEN_FULLSCREEN","0");
    sprintf(temp_string,"%d",DEFAULT_SCREEN_X);
    cfg_set(temp,"SCREEN_X",temp_string);
    sprintf(temp_string,"%d",DEFAULT_SCREEN_Y);
    cfg_set(temp,"SCREEN_Y",temp_string);
    cfg_set(temp,"SCREEN_DEPTH","16");

    // Audio settings
    sprintf(temp_string,"%d",MIX_DEFAULT_FREQUENCY);
    cfg_set(temp,"AUDIO_RATE",temp_string);
    sprintf(temp_string,"%d",MIX_DEFAULT_FORMAT);
    cfg_set(temp,"AUDIO_DEPTH",temp_string);
    sprintf(temp_string,"%d",MIX_DEFAULT_CHANNELS);
    cfg_set(temp,"AUDIO_CHANNELS",temp_string);
    sprintf(temp_string,"%d",MIX_DEFAULT_CHUNKSIZE);
    cfg_set(temp,"AUDIO_BUFFER",temp_string);
    sprintf(temp_string,"%d",MIX_MAX_VOLUME);
    cfg_set(temp,"AUDIO_SFX_VOLUME",temp_string);
    sprintf(temp_string,"%d",MIX_MAX_VOLUME);
    cfg_set(temp,"AUDIO_MUSIC_VOLUME",temp_string);

    return temp;
}

void init_opengl(int depth)
{
	#warning TODO: A lot of these values should be switchable from config file.

    if (depth == 24) {
        SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); // ask for at least 8,8,8
        SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
        SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
        SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 24); // overall buffer size (16 or 32 bits?)
    } else {
        SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); // ask for at least 5,5,5
        SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
        SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
        SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 16); // overall buffer size (16 or 32 bits?)
    }

    SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); // All alpha-blending is done using SOURCE alpha: no dest-alpha needed.

	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); // 16-bit depth precision ought to be enough
	SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0); // Stencil buffer is not used (though we may add it later for shadows)

    SDL_GL_SetAttribute(SDL_GL_ACCUM_RED_SIZE, 0);    // Accumulator settings.  MisCopter does not use Accumulator Buffer.
    SDL_GL_SetAttribute(SDL_GL_ACCUM_GREEN_SIZE, 0);
    SDL_GL_SetAttribute(SDL_GL_ACCUM_BLUE_SIZE, 0);
    SDL_GL_SetAttribute(SDL_GL_ACCUM_ALPHA_SIZE, 0);

	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); //Enable double buffering
	SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); //Enable Vsync
	SDL_GL_SetAttribute(SDL_GL_STEREO, 0); // Disable Stereo buffers

    SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS,  0); /* Disable FSAA */
    SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES,  0); /* number of multisample samples (2, 4, 8...) */
}

int main(int argc, char *argv[])
{
	// ---------------------------------------------------------------------
	// This makes relative paths work in C++ in Xcode by changing directory
	//  to the Resources folder inside the .app bundle
#ifdef __APPLE__
    CFBundleRef mainBundle = CFBundleGetMainBundle();
    CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
    char path[PATH_MAX];
    if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path, PATH_MAX))
    {
        // error!
    }
    CFRelease(resourcesURL);
    chdir(path);
    //std::cout << "Current Path: " << path << std::endl;
#endif
	// ---------------------------------------------------------------------

	// Initialize SDL
	if(SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO ) == -1)
	{
		fprintf(stderr, "Failed to initialize SDL: %s\n", SDL_GetError());
		exit(1);
	}
	// Register SDL_Quit at exit
	atexit(SDL_Quit);

    // Initialize new config struct to defaults
    cfg = set_cfg_defaults();
    // Load more options from cfg file
    cfg_load(cfg,"config.ini");

    // Go look up Screen Depth to set BPP
    if (atoi(cfg_get(cfg,"SCREEN_DEPTH")) >= 24)
    {
	    cfg_set(cfg,"SCREEN_DEPTH","24");
        init_opengl(24);
    } else {
	    cfg_set(cfg,"SCREEN_DEPTH","16");
        init_opengl(16);
    }

	// Initilize the screen
    SDL_Surface *screen = SDL_SetVideoMode(atoi(cfg_get(cfg,"SCREEN_X")), atoi(cfg_get(cfg,"SCREEN_Y")), atoi(cfg_get(cfg,"SCREEN_DEPTH")),
                            SDL_OPENGL | (atoi(cfg_get(cfg,"SCREEN_FULLSCREEN")) ? SDL_FULLSCREEN : 0) ) ;

	if(screen == NULL){
		fprintf(stderr, "Unable to set video mode: %s\n", SDL_GetError());
		exit(1);
	}

    // Set title bar
	SDL_WM_SetCaption ("MisCopter", NULL);

    // Set up OpenGL viewport and (global) settings
	glViewport(0,0,atoi(cfg_get(cfg,"SCREEN_X")),atoi(cfg_get(cfg,"SCREEN_Y")));

	// Set the screen clear color to black, and turn a few OpenGL switches on/off.
	glClearColor(0.0f,0.0f,0.0f,1.0f);
	glAlphaFunc(GL_EQUAL,1);
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_CULL_FACE);
	glEnable(GL_TEXTURE_2D);

    // Compute aspect ratio
    aspect = (GLfloat)atoi(cfg_get(cfg,"SCREEN_X")) / atoi(cfg_get(cfg,"SCREEN_Y"));

    // Go ahead and print OpenGL info to a log
	printf("OpenGL information:\nVendor: %s\nRenderer: %s\nVersion: %s\nExtensions: %s\n",
			glGetString( GL_VENDOR ),
			glGetString( GL_RENDERER ),
			glGetString( GL_VERSION ),
			glGetString( GL_EXTENSIONS ) );
	GLint GLTexSize = 0;
	glGetIntegerv( GL_MAX_TEXTURE_SIZE, & GLTexSize ) ;
	printf("Max texture size: %d\n",GLTexSize);
	printf("Screen:\t%dx%dx%d, aspect = %f\n", screen->w, screen->h,screen->format->BitsPerPixel,aspect);

	/* IMAGE INITIALIZATION */
	image_flags = IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG | IMG_INIT_TIF);
    if ( image_flags != (IMG_INIT_JPG | IMG_INIT_PNG | IMG_INIT_TIF))
	{
		printf("IMG_Init: failed to init jpeg/png/tiff support: %s\n",IMG_GetError());
	}
	// Register IMG_Quit at exit
	atexit(IMG_Quit);

	/* MIXER INITIALIZATION */
	mix_flags = Mix_Init(MIX_INIT_FLAC | MIX_INIT_MP3 | MIX_INIT_OGG);
    if ( mix_flags != (MIX_INIT_FLAC | MIX_INIT_MP3 | MIX_INIT_OGG))
	{
		printf("Mix_Init: failed to init flac/mp3/ogg support: %s\n",Mix_GetError());
	}
	// Register Mix_Quit at exit
	atexit(Mix_Quit);
	if(Mix_OpenAudio(atoi(cfg_get(cfg,"AUDIO_RATE")),atoi(cfg_get(cfg,"AUDIO_DEPTH")), atoi(cfg_get(cfg,"AUDIO_CHANNELS")), atoi(cfg_get(cfg,"AUDIO_BUFFER")))) {
		printf("Unable to open audio!\n");
		cfg_set(cfg,"AUDIO_SFX_VOLUME","0");
		cfg_set(cfg,"AUDIO_MUSIC_VOLUME","0");
	} else {
    	// Register Mix_CloseAudio at exit
    	atexit(Mix_CloseAudio);
    	/* Load CHUNKS */
    	sfx_click = load_wave("menu");
    }

/* #ifdef USE_NET
	// Initialize SDL_net
	if (SDLNet_Init() < 0)
	{
		fprintf(stderr, "SDLNet_Init: %s\n", SDLNet_GetError());
		exit(EXIT_FAILURE);
	}
	atexit(SDLNet_Quit);
#endif */

    ///////////////////////
    // ALL DONE LOADING LIBRARIES.
    //  Do regular game init.

	// Custom mouse cursor.  This is better than the standard anyway, because
	//  sometimes with a GL or SDL_HWSurface the built-in cursor can disappear.
	GLuint cursor = load_texture("cursor",GL_LINEAR,NULL,NULL);
	/* make a cursor list */
    cursor_list = glGenLists(1);
	glNewList(cursor_list, GL_COMPILE);
		use_texture(cursor);
		glBox(0,0,12,21,0,0,16,32);
	glEndList();

	SDL_ShowCursor(SDL_DISABLE);

	// Load the fonts.
	font[0] = load_font("data/font/moneygge.def");
	font[1] = load_font("data/font/sans_s_3.def");
	font[2] = load_font("data/font/sans_m_3.def");
	font[3] = load_font("data/font/sans_l_3.def");

	// Set the initial gamestate to the TITLE screen
	int gamestate = GAMESTATE_INTRO1;

	while(gamestate != GAMESTATE_EXIT)
	{
		switch(gamestate)
		{
			case GAMESTATE_INTRO1:
				gamestate = movie_main("intro1",GAMESTATE_INTRO2);
				break;
			case GAMESTATE_INTRO2:
				gamestate = movie_main("intro2",GAMESTATE_MENU);
				break;
			case GAMESTATE_MENU:
				gamestate = ui_main(menu_screen);
				break;
            case GAMESTATE_CAREER:
                gamestate = ui_main(career_screen[0]);
                break;
            case GAMESTATE_CAREER_MOVIE:
                gamestate = movie_main(career_movie, GAMESTATE_INIT_GAME);
                break;
            case GAMESTATE_INIT_GAME:
                gamestate = init_game_main("hello");
                break;
			case GAMESTATE_CREDITS:
				gamestate = ui_main(credits_screen);
				break;
/*			case GAMESTATE_GAME:
				gamestate = game_main();
				break;
			case GAMESTATE_HANGAR:
				gamestate = hangar_main();
				break;*/
			case GAMESTATE_ERROR:
				gamestate = error_main();
				break;
			default:
				sprintf(error_message, "Game reached unknown gamestate %d!",gamestate);
				gamestate = GAMESTATE_ERROR;
		}
	}

    // Dump cfg-struct to disk.
    cfg_save(cfg,"config.ini");
    // cleanup cfg-struct
    cfg_free(cfg);

	// fonts
	free_font(font[3]);
	free_font(font[2]);
	free_font(font[1]);
	free_font(font[0]);

	// shared mouse cursor
	glDeleteLists(cursor_list, 1);
	glDeleteTextures( 1, &cursor );
	SDL_ShowCursor( SDL_ENABLE );

	// if you have globally-shared sounds, it is wise to free them here
	// using Mix_FreeChunk(sound);
	if (sfx_click) Mix_FreeChunk(sfx_click);

    // Success.  This will also trigger registered exit handlers for libraries.
	return 0;
}
